﻿using System;
using System.Threading;


namespace advadev.CanOpen
{

    /// <summary>
    /// Represents a service timer
    /// </summary>
    public class ServiceTimer : IServiceTimer
    {

        /// <summary>for internal use</summary>
        private bool? _isStarted;


        /// <summary>
        /// Initializes a new instance of the ServiceTimer class
        /// </summary>
        public ServiceTimer()
        {
            FrameSize = new TimeSpan(0, 0, 0, 0, 1);
        }


        /// <summary>
        /// Gets or sets the frame size
        /// </summary>
        public TimeSpan FrameSize
        {
            get;
            set;
        }


        /// <summary>
        /// Raises the value changed event
        /// </summary>
        /// <param name="args">
        /// Event argument
        /// </param>
        private void onRaiseTimeFrameStarted(ServiceTimerTimeFrameStartedEventArgs args)
        {
            if (_timeFrameStarted != null)
            {
                _timeFrameStarted(this, args);
            }
        }

        /// <summary>
        /// Fits the given date time to the (beginning of the) current time
        /// frame
        /// </summary>
        /// <param name="dateTime">
        /// Date time
        /// </param>
        /// <returns>
        /// Date time fitted to the current time frame
        /// </returns>
        private DateTime fitToTimeFrame(DateTime dateTime)
        {
            return new DateTime((long)Math.Floor((double)dateTime.Ticks / (double)FrameSize.Ticks) * FrameSize.Ticks);
        }

        // -->> HACK
        static private string timeToLogString(DateTime time)
        {
            long floorTicks = (long)Math.Floor((double)time.Ticks / (double)TimeSpan.TicksPerMillisecond) * TimeSpan.TicksPerMillisecond;
            long microSeconds = ((time.Ticks - floorTicks) / (TimeSpan.TicksPerMillisecond / 1000));
            if (microSeconds == 1000)
            {
                microSeconds = 0;
            }
            return time.ToLongTimeString() + "." + string.Format("{0:000}", time.Millisecond) + "." + string.Format("{0:000}", microSeconds);
        }
        // <<-- HACK


        #region IServiceTimer implementation
        /// <summary>for internal use</summary>
        private event ServiceTimerTimeFrameStartedEventHandler _timeFrameStarted;

        /// <summary>
        /// Occures when a time frame is started
        /// </summary>
        public event ServiceTimerTimeFrameStartedEventHandler TimeFrameStarted
        {
            add
            {
                _timeFrameStarted += value;
            }
            remove
            {
                _timeFrameStarted -= value;
            }
        }

        /// <summary>
        /// Starts the timer
        /// </summary>
        public void Start()
        {
            if (_isStarted == null)
            {
                _isStarted = true;
                Thread timerThread = new Thread(new ThreadStart(() =>
                {
                    DateTime startFrameTime = fitToTimeFrame(DateTime.UtcNow);
                    DateTime nextFrameTime = startFrameTime;
                    while (_isStarted == true)
                    {
                        DateTime next = fitToTimeFrame(DateTime.UtcNow + FrameSize);
                        int counter = 2;
                        while (next <= nextFrameTime)
                        {
                            next = fitToTimeFrame(DateTime.UtcNow + new TimeSpan(counter++ * FrameSize.Ticks));
                        }
                        nextFrameTime = next;
                        Thread.Sleep(nextFrameTime - DateTime.UtcNow);
                        onRaiseTimeFrameStarted(new ServiceTimerTimeFrameStartedEventArgs(nextFrameTime, FrameSize));
                    }
                    _isStarted = null;
                }));
                timerThread.Start();
            }
        }

        /// <summary>
        /// Stops the timer
        /// </summary>
        public void Stop()
        {
            if (_isStarted == true)
            {
                _isStarted = false;
            }
        }
        #endregion IServiceTimer implementation

    }

}
